/**
 * @file jpeg.c
 gcc -g -D test_jpeg jpeg.c jcapistd.c rdbmp.c rdtarga.c jerror.c jcparam.c jdatadst.c jcapimin.c jcomapi.c jmemmgr.c jcmarker.c jcinit.c jcmaster.c jutils.c jmemansi.c jccolor.c jcprepct.c jchuff.c jcsample.c jcdctmgr.c jccoefct.c jfdctint.c jfdctflt.c jfdctfst.c jcmainct.c jcarith.c jaricom.c && ./a.out

 * @author db0@qq.com
 * @version 1.0.1
 * @date 2017-10-19
 */
#include "string.h"
#include "stdlib.h"
#include "stdio.h"
#include "jpeglib.h"

unsigned char * GeneJpegFile(unsigned long * outlen, unsigned char* inputData,
		int nWidth, int nHeight, int nChannel, int nQuality)
{
	/* This struct contains the JPEG compression parameters and pointers to
	 * working space (which is allocated as needed by the JPEG library).
	 * It is possible to have several such structures, representing multiple
	 * compression/decompression processes, in existence at once.  We refer
	 * to any one struct (and its associated working data) as a "JPEG object".
	 */
	struct jpeg_compress_struct cinfo;

	/* This struct represents a JPEG error handler.  It is declared separately
	 * because applications often want to supply a specialized error handler
	 * (see the second half of this file for an example).  But here we just
	 * take the easy way out and use the standard error handler, which will
	 * print a message on stderr and call exit() if compression fails.
	 * Note that this struct must live as long as the main JPEG parameter
	 * struct, to avoid dangling-pointer problems.
	 */
	struct jpeg_error_mgr jerr;

	/* More stuff */
	JSAMPROW row_pointer[1];        /* pointer to JSAMPLE row[s] */
	int     row_stride;             /* physical row width in image buffer */

	/* Step 1: allocate and initialize JPEG compression object */

	/* We have to set up the error handler first, in case the initialization
	 * step fails.  (Unlikely, but it could happen if you are out of memory.)
	 * This routine fills in the contents of struct jerr, and returns jerr's
	 * address which we place into the link field in cinfo.
	 */
	cinfo.err = jpeg_std_error(&jerr);

	/* Now we can initialize the JPEG compression object. */
	jpeg_create_compress(&cinfo);  /* Now we can initialize the JPEG compression object. */

	/* Step 2: specify data destination (eg, a file) */
	/* Note: steps 2 and 3 can be done in either order. */

	/* Here we use the library-supplied code to send compressed data to a
	 * stdio stream.  You can also write your own code to do something else.
	 * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
	 * requires it in order to write binary files.
	 */
	//jpeg_stdio_dest(&cinfo, outData,nWidth*nHeight*nChannel);
	unsigned char * outData = malloc(nWidth*nHeight*nChannel);
	jpeg_mem_dest(&cinfo, &outData,outlen);

	/* Step 3: set parameters for compression */

	/* First we supply a description of the input image.
	 * Four fields of the cinfo struct must be filled in:
	 */
	cinfo.image_width = nWidth;                /* image width and height, in pixels */
	cinfo.image_height = nHeight;
	cinfo.input_components = nChannel;         /* # of color components per pixel */

	if(nChannel == 1){
		cinfo.in_color_space = JCS_GRAYSCALE;  /* colorspace of input image */
	}
	else if (nChannel == 3)
	{
		cinfo.in_color_space = JCS_RGB;        /* colorspace of input image */
	}

	/* Now use the library's routine to set default compression parameters.
	 * (You must set at least cinfo.in_color_space before calling this,
	 * since the defaults depend on the source color space.)
	 */
	jpeg_set_defaults(&cinfo);

	// Now you can set any non-default parameters you wish to.
	// Here we just illustrate the use of quality (quantization table) scaling:
	jpeg_set_quality(&cinfo, nQuality, TRUE); /* limit to baseline-JPEG values */

	/* Step 4: Start compressor */

	/* TRUE ensures that we will write a complete interchange-JPEG file.
	 * Pass TRUE unless you are very sure of what you're doing.
	 */
	jpeg_start_compress(&cinfo, TRUE);

	/* Step 5: while (scan lines remain to be written) */
	/*           jpeg_write_scanlines(...); */

	/* Here we use the library's state variable cinfo.next_scanline as the
	 * loop counter, so that we don't have to keep track ourselves.
	 * To keep things simple, we pass one scanline per call; you can pass
	 * more if you wish, though.
	 */

	row_stride = nWidth * nChannel;
	while (cinfo.next_scanline < cinfo.image_height)
	{
		row_pointer[0] = &inputData[cinfo.next_scanline * row_stride];
		(void)jpeg_write_scanlines(&cinfo, row_pointer, 1);
	}

	jpeg_finish_compress(&cinfo);
	jpeg_destroy_compress(&cinfo);

	return outData;

	/* After finish_compress, we can close the output file. */
}

#ifdef test_jpeg
int main()
{
	int nWidth=300;
	int nHeight=200;
	int nChannel=4;
	int nQuality=100;
	unsigned long size = (nWidth*nHeight*nChannel);
	unsigned char * inData = malloc(size);
	memset(inData,0xcc,size);
	int i = 0;
	while(i<nWidth)
	{
		int j = 0;
		while(j<nHeight)
		{
			if(i==j)
			{
				inData[(i+j*nWidth)*nChannel] = 0xff;
				inData[(i+j*nWidth)*nChannel+1] = 0xff;
				inData[(i+j*nWidth)*nChannel+2] = 0x00;
				inData[(i+j*nWidth)*nChannel+3] = 0xff;
			}
			++j;
		}
		++i;
	}
	unsigned long outlen=0;
	unsigned char * outData = NULL;
	outData = GeneJpegFile(&outlen, inData, nWidth, nHeight, nChannel, nQuality);

	FILE * file = fopen("test.jpeg","wb");
	fwrite(outData,outlen,1,file);
	fclose(file);

	return 0;
}
#endif


#ifdef as_api
#include "bytearray.h"
#include "AS3.h"

static AS3_Val encode(void* self, AS3_Val args)
{
	AS3_Val byteArray;
	//AS3_Val string;
	AS3_Val width;
	AS3_Val height;
	AS3_Val quality;
	AS3_Val outBytes=NULL;
	//var bytes:ByteArray = lib.encode(w,h,bmpdBytes,80);
	AS3_ArrayValue(args, "AS3ValType,AS3ValType,AS3ValType,AS3ValType,AS3ValType", &width,&height,&byteArray,&quality,&outBytes);
	AS3_Val length = AS3_GetS(byteArray, "length");  
	//AS3_Trace(length);

	int len = AS3_IntValue(length);
	AS3_Release(length);
	int w = AS3_IntValue(width);
	AS3_Release(width);
	int h = AS3_IntValue(height);
	AS3_Release(height);
	int nQuality = AS3_IntValue(quality);
	AS3_Release(quality);
	unsigned char * inData=malloc(len);
	//int AS3_ByteArray_seek(AS3_Val dst, int offs, int whence)：
	AS3_ByteArray_seek(byteArray,0,0);
	AS3_ByteArray_readBytes(inData,byteArray, len);

	AS3_Release(byteArray);

	//ByteArray * bytearray = ByteArray_new(bufferLen);
	//bytearray->inData = (char*)inData;

	unsigned long int outlen = 0;
	unsigned char * in = malloc(len*3/4);
	//ZipFile_free(ZipFile_parser(bytearray,s,out,&outlen));
	int i=0;
	int j=0;
	for(i=0;i<h;i++)
	{
		for(j=0;j<w;j++)
		{
			in[(i*w+j)*3+0] = inData[(i*w+j)*4+1]&0xff;
			in[(i*w+j)*3+1] = inData[(i*w+j)*4+2]&0xff;
			in[(i*w+j)*3+2] = inData[(i*w+j)*4+3]&0xff;
		}
	}
	unsigned char * out = GeneJpegFile(&outlen, in, w, h, 3, nQuality);

	//ByteArray_free(bytearray);

	if(outBytes==NULL){
		AS3_Val baNS = AS3_String("flash.utils");
		AS3_Val baClass = AS3_NSGetS(baNS, "ByteArray");
		AS3_Val emptyParams = AS3_Array("");
		outBytes = AS3_New(baClass, emptyParams); 
		AS3_Release(baNS);
		AS3_Release(baClass);
		AS3_Release(emptyParams);
	}


	AS3_Val position_property = AS3_String("position");  
	AS3_Val zero = AS3_Int(0);  
	AS3_Set(outBytes, position_property, zero);  
	AS3_Release(zero);  
	AS3_Release(position_property);  

	//sprintf(out+strlen(out),"[%d]%d,%d",bufferLen,strlen(out),outlen);
	AS3_ByteArray_writeBytes(outBytes,out,outlen);
	free(in);
	free(inData);
	free(out);

	return outBytes;
}

int main()
{
	//define the methods exposed to ActionScript
	//typed as an ActionScript Function instance
	AS3_Val encodeMethod = AS3_Function( NULL, encode);

	// construct an object that holds references to the functions
	AS3_Val result = AS3_Object( "encode: AS3ValType", encodeMethod);

	// Release
	AS3_Release(encodeMethod);

	// notify that we initialized -- THIS DOES NOT RETURN!
	AS3_LibInit( result );

	// should never get here!
	return 0;
}
#endif



